package com.xbongbong.paas.controller;

import com.alibaba.fastjson.JSON;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasFormEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.wrap.XbbResponse;
import com.xbongbong.pro.aiassistant.config.AssistantThreadPoolBeanConfig;
import com.xbongbong.pro.aiassistant.service.AiAssistantStrategyService;
import com.xbongbong.pro.aiassistant.service.GenerateDataService;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.constant.XbbProConstant;
import com.xbongbong.pro.domain.entity.AiAssistantEntity;
import com.xbongbong.pro.enums.CustomStrategyTypeEnum;
import com.xbongbong.pro.enums.ValueAddedTypeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.smartsettings.enums.StrategyTypeEnum;
import com.xbongbong.pro.service.toolbox.smartsettings.help.SmartSettingsHelp;
import com.xbongbong.pro.smartsettings.pojo.SmartStrategyPojo;
import com.xbongbong.pro.smartsettings.pojo.dto.GenerateDataDTO;
import com.xbongbong.pro.smartsettings.pojo.dto.GetHistoryDTO;
import com.xbongbong.pro.smartsettings.pojo.dto.RunStrategyDTO;
import com.xbongbong.pro.smartsettings.pojo.dto.SmartSettingsUpdateBatchDTO;
import com.xbongbong.pro.smartsettings.pojo.dto.SmartStrategyRabbitMqDTO;
import com.xbongbong.pro.smartsettings.pojo.dto.TestDTO;
import com.xbongbong.pro.smartsettings.pojo.vo.GetHistoryVO;
import com.xbongbong.pro.smartsettings.pojo.vo.SmartSettingsUpdateBatchVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.model.AiAssistantModel;
import com.xbongbong.saas.service.toolbox.help.UpdatePackageRedisService;
import com.xbongbong.sys.domain.entity.CompanyEntity;
import com.xbongbong.sys.help.SysEnvHelp;
import com.xbongbong.sys.model.CompanyModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 智能策略
 *
 * @Author: hongxiao
 * @date: 2019-11-11 21:39
 */
@RestController
@RequestMapping(XbbProConstant.API_ROOT_PREFIX + "/script/aiAssistant")
public class AiAssistantStrategyController {
    private static final Logger LOG = LoggerFactory.getLogger(AiAssistantStrategyController.class);
    @Resource
    private ProBaseConfig proBaseConfig;
    @Resource
    private AiAssistantModel aiAssistantModel;
    @Resource
    private AiAssistantStrategyService aiAssistantStrategyService;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private CompanyModel companyModel;
    @Resource
    private GenerateDataService generateCustomerData;
    @Resource
    private SmartSettingsHelp smartSettingsHelp;
    @Resource
    private UpdatePackageRedisService updatePackageRedisService;
    @Resource
    private AssistantThreadPoolBeanConfig assistantThreadPoolBeanConfig;
    @Resource
    private SysEnvHelp sysEnvHelp;

    @RequestMapping(value = "/runStrategy", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String runStrategy(@Valid RunStrategyDTO runStrategyDTO, BindingResult br) throws XbbException {
        XbbResponse<Object> response;

        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            try {
                if (!proBaseConfig.getXbbCode().equals(runStrategyDTO.getXbbCode())) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
                }
                // 是否测试单一公司
                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                param.put("del", DelEnum.NORMAL.getDel());
                param.put("enable", BasicConstant.IS_USE);
                if(runStrategyDTO.getIsTest()) {
                    List<String> corpIds = runStrategyDTO.getTestCorpIds();
                    if(Objects.isNull(corpIds) || corpIds.isEmpty()) {
                        return "测试时请添加需要测试的公司id";
                    }
                    param.put("corpidIn", corpIds);
                    if (Objects.nonNull(runStrategyDTO.getTestAiAssistantIds()) && !runStrategyDTO.getTestAiAssistantIds().isEmpty()) {
                        param.put("idIn", runStrategyDTO.getTestAiAssistantIds());
                        param.remove("enable");
                    }
                }
                // 获取启用的智能助手公司
                List<String> corpidList = aiAssistantModel.findCorps(param);

                // 过滤无效公司（解绑，套餐过期）
                Long now = DateUtil.getNow();
                boolean isGrayEnvironmentEnable = sysEnvHelp.isGrayEnvironmentEnable();
                List<CompanyEntity> companyList = companyModel.getCompanyList(corpidList);
                for (CompanyEntity company : companyList) {

                    if (sysEnvHelp.isGrayEnvironment()) {
                        if (!isGrayEnvironmentEnable) {
                            continue;
                        }
                        if (!sysEnvHelp.checkIsCrayCompany(company.getCorpid())) {
                            continue;
                        }
                    } else {
                        if (isGrayEnvironmentEnable) {
                            if (sysEnvHelp.checkIsCrayCompany(company.getCorpid())) {
                                continue;
                            }
                        }
                    }

                    Long endTime = company.getFeeEndTime();
                    if (!Objects.equals(BasicConstant.ONE, company.getStatus()) || endTime < now) {
                        corpidList.remove(company.getCorpid());
                    }

                    boolean hasAssistantMenu = smartSettingsHelp.hasMenu(company.getCorpid());
                    if (!hasAssistantMenu) {
                        // 非白名单公司，不执行策略
                        corpidList.remove(company.getCorpid());
                    }
                }
                // 将阁润放到最后一个执行公司
                String noCorpid = "ding9ab8ee5bfe81d537";
                if (corpidList.contains(noCorpid)) {
                    corpidList.remove(noCorpid);
                    corpidList.add(noCorpid);
                }

                run(corpidList, runStrategyDTO);

                LOG.info("end assistant");
                response = new XbbResponse<>();
            } catch (XbbException e) {
                response = new XbbResponse<>(e);
            }
        }
        return JSON.toJSONString(response);
    }

    void run(List<String> corpidList, RunStrategyDTO runStrategyDTO) throws XbbException{
        Runnable runnable = () -> {
            try {
                SmartStrategyRabbitMqDTO rabbitMqDTO = new SmartStrategyRabbitMqDTO();
                for(String corpid : corpidList) {
                    LOG.info("corpid :" + corpid + "；开始执行智能助手");
                    Integer num = updatePackageRedisService.getUnusedNumPackageLimit(corpid, ValueAddedTypeEnum.AI_ASSISTANT);
                    if (num > 0) {
                        LOG.info("当前套餐超过限制，智能助手不在执行；corpid： " + corpid);
                        continue;
                    }
                    Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    param.put("del", DelEnum.NORMAL.getDel());
                    param.put("enable", BasicConstant.IS_USE);
                    if (CollectionsUtil.isNotEmpty(runStrategyDTO.getTestAiAssistantIds())) {
                        param.put("idIn", runStrategyDTO.getTestAiAssistantIds());
                        param.remove("enable");
                    }
                    param.put(StringConstant.CORPID, corpid);
                    List<AiAssistantEntity> enableList = aiAssistantModel.findEntitys(param);
                    if (CollectionsUtil.isNotEmpty(enableList)) {
                        for (AiAssistantEntity aiAssistantEntity: enableList) {
                            if (!filterThatDaySmartSetting(aiAssistantEntity) || filterDisableForm(aiAssistantEntity, corpid) || isInstockOrOutstock(aiAssistantEntity.getBusinessType())) {
                                LOG.info("corpid :" + corpid + "；" + aiAssistantEntity.getName() + " 无可执行的策略数据");
                                continue;
                            }
                            LOG.info("start assistant corpid :" + corpid + "；" + aiAssistantEntity.getName() + " 开始执行");
                            rabbitMqDTO = new SmartStrategyRabbitMqDTO();
                            rabbitMqDTO.setCorpid(corpid);
                            rabbitMqDTO.setAiAssistantEntity(aiAssistantEntity);
                            aiAssistantStrategyService.newAiAssistantStrategy(rabbitMqDTO, runStrategyDTO);
                        }
                    }
                }
            } catch (Exception e) {
                LOG.error("run assistant error", e);
            }
        };
        assistantThreadPoolBeanConfig.runAssistant().execute(runnable);

    }
    /**
     * 短信入库
     */
    @RequestMapping(value = "/testSms", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String testSms(@RequestBody @Valid TestDTO testDTO, BindingResult br) throws XbbException {
        XbbResponse<String> response;

        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            response = new XbbResponse<>(generateCustomerData.testSms(testDTO.getCorpid(), testDTO.getReceiveUserId(), testDTO.getId(), testDTO.getDataIds(), testDTO.getBusinessType()));
        }
        return JSON.toJSONString(response);

    }

    /**
     * 访客计划入库
     */
    @RequestMapping(value = "/testCommunicatePlan", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String testCommunicatePlan(@RequestBody @Valid TestDTO testDTO, BindingResult br) throws XbbException {
        XbbResponse<String> response;

        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            response = new XbbResponse<>(generateCustomerData.testCommunicatePlan(testDTO.getCorpid(), testDTO.getReceiveUserId(), testDTO.getId(), testDTO.getDataIds(), testDTO.getBusinessType()));
        }
        return JSON.toJSONString(response);
    }


    /**
     * 生成客户数据
     */
    @RequestMapping(value = "/generateData", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String generateCustomerData(@RequestBody @Valid GenerateDataDTO generateDataDTO, BindingResult br) throws XbbException {
        XbbResponse<String> response;

        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            if (!proBaseConfig.getXbbCode().equals(generateDataDTO.getXbbCode())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
            }
            String successMsg = generateCustomerData.generateData(generateDataDTO);
            response = new XbbResponse<>(successMsg);
        }
        return JSON.toJSONString(response);

    }

    /**
     * 批量更新
     *
     * @param updateBatchDTO 入参
     * @param br
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-06 10:45
     * @since
     * @version
     */
    @RequestMapping(value = "/updateBatch", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String updateBatch(@RequestBody @Valid SmartSettingsUpdateBatchDTO updateBatchDTO, BindingResult br) {
        XbbResponse<SmartSettingsUpdateBatchVO> response;
        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            try {
                if (!proBaseConfig.getXbbCode().equals(updateBatchDTO.getXbbCode())) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
                }
                SmartSettingsUpdateBatchVO updateBatchVO = generateCustomerData.updateBatch(updateBatchDTO);
                response = new XbbResponse<>(updateBatchVO);
            } catch (XbbException e) {
                response = new XbbResponse<>(e);
            }
        }
        return JSON.toJSONString(response);
    }

    /**
     * 获取入库记录
     *
     * @param br
     * @param getHistoryDTO 入参
     * @throws
     * @return
     * @author hongxiao
     * @date 2019-12-10 14:16
     * @since
     * @version
     */
    @RequestMapping(value = "/getHistory", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String getHistory(@RequestBody @Valid GetHistoryDTO getHistoryDTO, BindingResult br) {
        XbbResponse<GetHistoryVO> response;
        if (br.hasErrors()) {
            String errMsg = Objects.requireNonNull(br.getFieldError()).getDefaultMessage();
            response = new XbbResponse<>(SystemErrorCodeEnum.API_ERROR_100002, errMsg);
        } else {
            try {
                if (!proBaseConfig.getXbbCode().equals(getHistoryDTO.getXbbCode())) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100006);
                }
                GetHistoryVO getHistoryVO = generateCustomerData.getHistory(getHistoryDTO);
                response = new XbbResponse<>(getHistoryVO);
            } catch (XbbException e) {
                response = new XbbResponse<>(e);
            }
        }
        return JSON.toJSONString(response);
    }

    /**
     * 过虑未当天执行的策略
     *
     * @param list 智能助手列表
     * @return
     * @throws
     * @author hongxiao
     * @date: 2019-05-09 09:57
     * @version v1.0
     * @since v1.0
     */
    private void filterSmartSettings(List<AiAssistantEntity> list) {
        if (Objects.isNull(list) || list.isEmpty()) {
            return;
        }
        Iterator<AiAssistantEntity> it = list.iterator();
        while (it.hasNext()) {
            AiAssistantEntity entity = it.next();

            // 是否在执行日期内
            boolean inDateFlag = filterThatDaySmartSetting(entity);
            // 不在执行日期内移除
            if (!inDateFlag) {
                LOG.info("corpid :" + entity.getCorpid() + "；无法执行智能助手；策略id="+entity.getId() + "（"+entity.getName()+"）");
                it.remove();
            }
        }
    }

    private boolean filterThatDaySmartSetting(AiAssistantEntity entity) {
        // 执行策略
        SmartStrategyPojo strategyPojo = entity.getStrategy().toJavaObject(SmartStrategyPojo.class);
        // 是否在执行日期内
        boolean inDateFlag = false;
        // 执行日期
        StrategyTypeEnum typeEnum = StrategyTypeEnum.getByCode(strategyPojo.getStrategyType());
        if(Objects.nonNull(typeEnum)) {
            switch (typeEnum) {
                // 每周
                case WEEKLY:
                    inDateFlag = checkWeekly(strategyPojo.getStrategyWeek());
                    break;
                // 周月
                case MONTHLY:
                    inDateFlag = checkMonthly(strategyPojo.getStrategyDay());
                    break;
                // 每年
                case ANNUALLY:
                    inDateFlag = checkAnnually(strategyPojo.getStrategyDay());
                    break;
                // 自定义
                case CUSTOM:
                    CustomStrategyTypeEnum customTypeEnum = CustomStrategyTypeEnum.getByCode(strategyPojo.getCustomType());
                    if (customTypeEnum == CustomStrategyTypeEnum.WEEKLY) {
                        inDateFlag = checkWeekly(strategyPojo.getStrategyWeek());
                    } else if (customTypeEnum == CustomStrategyTypeEnum.MONTHLY) {
                        inDateFlag = checkMonthly(strategyPojo.getStrategyDay());
                    }
                    break;
                // 每天、只执行一次
                default:
                    inDateFlag = true;
                    break;
            }
        }
        return inDateFlag;
    }
    /**
     * 当前是否为执行的星期（星期几）
     *
     * @param strategyWeek 星期对应的数字
     * @return
     * @throws
     * @author hongxiao
     * @date: 2019-05-09 09:55
     * @version v1.0
     * @since v1.0
     */
    private boolean checkWeekly(Integer strategyWeek) {
        int week = DateUtil.getWeekInSomeday(DateUtil.getInt());
        return strategyWeek - week == 1;
    }

    /**
     * 当前是否为执行的日期（几号）
     * 执行日期：2月大于28/29（执行日期为2月最后一天），小月大于30（执行日期为当月最后一天）
     *
     * @param strategyDay 执行的日期（几号）
     * @return
     * @throws
     * @author hongxiao
     * @date: 2019-05-09 09:55
     * @version v1.0
     * @since v1.0
     */
    private boolean checkMonthly(String strategyDay) {
        long currentDay = DateUtil.getTodayInt();
        long lastMonthDay = DateUtil.getLastDateOfThisMonth() - TimeConstant.SECONDS_PER_DAY;
        String currentMonth = DateUtil.getString(DateUtil.SDFMonth);
        long strategyTime = DateUtil.getInt(currentMonth + "-" + strategyDay, DateUtil.SDFDate);
        // 执行时间是否大于当月最后一天时间
        if (strategyTime > lastMonthDay) {
            strategyTime = lastMonthDay;
        }
        return currentDay == strategyTime;
    }

    /**
     * 当前是否为执行的日期（几月几号）
     *
     * @param strategyDay 执行的日期（几月几号）
     * @return
     * @throws
     * @author hongxiao
     * @date: 2019-05-09 09:54
     * @version v1.0
     * @since v1.0
     */
    private boolean checkAnnually(String strategyDay) {
        String dayStr = DateUtil.getString(DateUtil.SDFMonthDay);
        return Objects.equals(dayStr, strategyDay);
    }

    /**
     * 过虑表单未启用的智能助手
     * @param list 智能助手列表
     * @return
     * @throws
     * @author hongxiao
     * @date 2019-10-15 19:32
     * @since v1.0
     * @version v1.0
     */
    private void filterDisableForms(List<AiAssistantEntity> list, String corpid) {
        if (Objects.isNull(list) || list.isEmpty()) {
            return;
        }

        // 所有表单id，不区分公司
        Set<Long> formIdSet = new HashSet<>();
        list.forEach(item -> {
            formIdSet.add(item.getFormId());
        });

        // 获取已启用的表单(所有公司）
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("del", DelEnum.NORMAL.getDel());
        param.put("enable", BasicConstant.IS_USE);
        param.put("columns", "id");
        param.put("idIn", formIdSet);
        param.put(StringConstant.CORPID, corpid);
        List<PaasFormEntityExt> enableFormList = paasFormModel.list(param);


        formIdSet.clear();
        for(PaasFormEntityExt entityExt : enableFormList) {
            formIdSet.add(entityExt.getId());
        }

        Iterator<AiAssistantEntity> it = list.iterator();
        while (it.hasNext()) {
            AiAssistantEntity entity = it.next();
            if(!formIdSet.contains(entity.getFormId())) {
                it.remove();
            }
        }
    }

    /**
     * 过滤掉未启用的表单
     * @param aiAssistantEntity
     * @param corpid
     * @return  true: 未启用，需要过滤；false：启用中
     */
    private boolean filterDisableForm(AiAssistantEntity aiAssistantEntity, String corpid) {

        Long formId = aiAssistantEntity.getFormId();
        PaasFormEntity paasFormEntity = paasFormModel.getByKey(formId, corpid);
        if (Objects.nonNull(paasFormEntity) && Objects.equals(1, paasFormEntity.getEnable())) {
            return false;
        }
        return true;
    }

    /**
     *
     * 过虑入库单、出库单
     * @param list 智能助手列表
     * @throws
     * @return
     * @author hongxiao
     * @date 2020-03-13 18:15
     * @since
     * @version
     */
    private void filterInstockAndOutstock(List<AiAssistantEntity> list) {
        if (Objects.isNull(list) || list.isEmpty()) {
            return;
        }

        Iterator<AiAssistantEntity> it = list.iterator();
        while (it.hasNext()) {
            AiAssistantEntity entity = it.next();
            Integer businessType = entity.getBusinessType();
            if(isInstockOrOutstock(businessType)) {
                it.remove();
            }
        }
    }

    private boolean isInstockOrOutstock(Integer businessType) {
       return Objects.equals(XbbRefTypeEnum.INSTOCK.getCode(), businessType) || Objects.equals(XbbRefTypeEnum.OTHER_INSTOCK.getCode(), businessType)
                || Objects.equals(XbbRefTypeEnum.PURCHASE_INSTOCK.getCode(), businessType) || Objects.equals(XbbRefTypeEnum.REFUND_INSTOCK.getCode(), businessType)
                || Objects.equals(XbbRefTypeEnum.RETURNED_MATERIEL_INSTOCK.getCode(), businessType) || Objects.equals(XbbRefTypeEnum.PRODUCTION_INSTOCK.getCode(), businessType)
                || Objects.equals(XbbRefTypeEnum.WAIT_INSTOCK_PURCHASE.getCode(), businessType)
                || Objects.equals(XbbRefTypeEnum.OUTSTOCK.getCode(), businessType) || Objects.equals(XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode(), businessType)
                || Objects.equals(XbbRefTypeEnum.OTHER_OUTSTOCK.getCode(), businessType) || Objects.equals(XbbRefTypeEnum.RETURNED_PURCHASE_OUTSTOCK.getCode(), businessType)
                || Objects.equals(XbbRefTypeEnum.WORK_ORDER_OUTSTOCK.getCode(), businessType) || Objects.equals(XbbRefTypeEnum.PRODUCTION_MATERIEL_OUTSTOCK.getCode(), businessType)
                || Objects.equals(XbbRefTypeEnum.WAIT_OUTSTOCK.getCode(), businessType);
    }
}
